<?php

/**
  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; version 3 of the License.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
 */
/**
 * AView abstract view classes
 * @package Core
 * @subpackage View
 * @category Abstract
 * @author kovacsricsi
 * @author Vincze Tam�s pilou@pilou.hu
 * @version 0.2  
 */

namespace Core\View;

abstract class AView implements IView {

	/**
	 * Header object for the view.
	 * @access protected
	 * @var \Core\HTTP\Header\IHeader
	 */
	protected $_header;

	/**
	 * Template directory.
	 * @access protected
	 * @var string
	 */
	protected $_templateDirectory;

	/**
	 * Template for view.
	 * @access protected
	 * @var string
	 */
	protected $_template;

	/**
	 * Layout for the view.
	 * @access protected
	 * @var string
	 */
	protected $_layout;

	/**
	 * Output string.
	 * @access protected
	 * @var string
	 */
	protected $_output;

	/**
	 * Template data.
	 * @access protected
	 * @var array
	 */
	protected $_data;

	/**
	 * Tags for the view. (CSS, JS in HEAD, JS before /BODY, inline js data in HEAD, meta)
	 * @access protected
	 * @var array
	 */
	protected $_addons;

	/**
	 * Title tag for view.
	 * @access protected
	 * @var Title
	 */
	protected $_title;

	/**
	 * Keywords tag for view.
	 * @access protected
	 * @var Keywords
	 */
	protected $_keywords;

	/**
	 * Keywords tag for view.
	 * @access protected
	 * @var Description
	 */
	protected $_description;

	/**
	 * Script vars helpers
	 * @var array of \Core\View\Helper\ScriptVars
	 */
	protected $_scriptVarsHelpers = array();

	/**
	 * Stores the strings to be injected: after <body> (perpendBody), before </body> (appendBody)
	 * @var array of \Core\View\Helper\ScriptVars
	 */
	protected $_layoutInjects = array();

	/**
	 * Constructor.
	 * @access public
	 * @param string $templateDirectory
	 * @param string $template
	 * @param string $layout
	 * @param string $title
	 * @return void
	 */
	public function __construct($templateDirectory = "", $template = "", $layout = "", $title = null) {
		$this->_templateDirectory = (string) $templateDirectory;
		$this->_template = (string) $template;
		$this->_layout = (string) $layout;
		$this->_title = ($title !== null) ? new Helper\Title((string) $title) : $title;
		$this->_addons = array();
		$this->_data = array();
		$this->_output = "";
	}

	/**
	 * Sets the header for the view
	 * @access public
	 * @param \Core\HTTP\Header\IHeader $headerObject 
	 */
	public function setHeader(\Core\HTTP\Header\IHeader $headerObject) {
		$this->_header = $headerObject;
	}

	/**
	 * Gets the header for the view
	 * @access public
	 * @return \Core\HTTP\IHeader
	 */
	public function getHeader() {
		return $this->_header;
	}

	/**
	 * Set template directory.
	 * @access public
	 * @param string $templateDirectory
	 * @return void
	 */
	public function setTemplateDirectory($templateDirectory) {
		$this->_templateDirectory = (string) $templateDirectory;
	}

	/**
	 * Set template for view.
	 * @access public
	 * @param string $template
	 * @return void
	 */
	public function setTemplate($template) {
		$this->_template = (string) $template;
	}

	/**
	 * Get template of view.
	 * @access public
	 * @return string
	 */
	public function getTemplate() {
		return $this->_template;
	}

	/**
	 * Set layout for view.
	 * @access public
	 * @param string $layout
	 * @return void
	 */
	public function setLayout($layout) {
		$this->_layout = (string) $layout;
	}

	/**
	 * Get layout of view.
	 * @access public
	 * @return string
	 */
	public function getLayout() {
		return $this->_layout;
	}

	/**
	 * Store template data.
	 * @access public
	 * @param string $name
	 * @param mixed $data
	 * @return void
	 */
	public function setVar($name, $data) {
		$this->$name = $data;
	}

	/**
	 * Get template data.
	 * @access public
	 * @param string $name
	 * @return mixed
	 */
	public function getVar($name) {
		$this->$name;
	}

	/**
	 * Store template data.
	 * @access public
	 * @param string $name
	 * @param mixed $data
	 * @return void
	 */
	public function __set($name, $data) {
		$this->_data[$name] = $data;
	}

	/**
	 * Get template data.
	 * @access public
	 * @param string $name
	 * @return mixed
	 */
	public function __get($name) {
		if (isset($this->_data[$name])) {
			return $this->_data[$name];
		} else {
			return null;
		}
	}

	/**
	 * Store template datas
	 * @param array $data assocative
	 * @return void
	 */
	public function setVars($data) {
		foreach ($data as $key => $value) {
			$this->$key = $value;
		}
	}

	/**
	 * Get all template data.
	 * @access public
	 * @return array
	 */
	public function getVars() {
		return $this->_data;
	}

	/**
	 * Set title for view.
	 * @access public
	 * @param string
	 * @return void
	 */
	public function setTitle($title) {
		$this->_title = new Helper\Title($title);
	}

	/**
	 * Set Google Analitics script for view.
	 * @access public
	 * @param string
	 * @return void
	 */
	public function setGoogleAnalytics($analyticsCode) {
		$this->_addons["googleAnalytics"] = new Helper\GoogleAnalytics($analyticsCode);
	}

	/**
	 * Set keywords for view.
	 * @access public
	 * @param string
	 * @return void
	 */
	public function setKeywords($keywords) {
		$this->_keywords = new Helper\Keywords($keywords);
	}

	/**
	 * Set keywords for view.
	 * @access public
	 * @param string
	 * @return void
	 */
	public function setDescription($description) {
		$this->_description = new Helper\Description($description);
	}
	
	/**
	 * Set string after <head> tag
	 * @access public
	 * @param string $string
	 * @param string $mode (replace|append|prepend)
	 */
	public function headPrepend($string, $mode = "replace"){
		$this->_layoutInject($string, $mode, "headPrepend");
	}
	
	/**
	 * Set string after <head> tag
	 * @access public
	 * @param string $string
	 * @param string $mode (replace|append|prepend)
	 */
	public function headAppend($string, $mode = "replace"){
		$this->_layoutInject($string, $mode, "headAppend");
	}

	/**
	 * Set string after <head> tag
	 * @access public
	 * @param string $string
	 * @param string $mode (replace|append|prepend)
	 */
	public function bodyPrepend($string, $mode = "replace"){
		$this->_layoutInject($string, $mode, "bodyPrepend");
	}
	
	/**
	 * Set string after <head> tag
	 * @access public
	 * @param string $string
	 * @param string $mode (replace|append|prepend)
	 */
	public function bodyAppend($string, $mode = "replace"){
		$this->_layoutInject($string, $mode, "bodyAppend");
	}
	
	/**
	 * Set string after <head> tag
	 * @access public
	 * @param string $string
	 * @param string $mode (replace|append|prepend)
	 * @param string $var name of the $_layoutInjects key
	 */
	protected function _layoutInject($string, $mode, $var){
		settype($string, "string");
		$this->_layoutInjects[$var] = "";
		switch ($mode) {
			case "append":
				$this->_layoutInjects[$var] = $this->_layoutInjects[$var] . $string; 
				break;
			case "prepend":
				$this->_layoutInjects[$var] = $string . $this->_layoutInjects[$var]; 
				break;
			default:
				$this->_layoutInjects[$var] = $string; 
				break;
		}
	}

	/**
	 * Returns title string.
	 * @access public
	 * @return string
	 */
	public function getTitle() {
		return (is_null($this->_title)) ? null : $this->_title->getTitle();
	}

	/**
	 * Script vars to head addons.
	 * @access public
	 * @param array|string $dataOrKey If array an associative array with key value pairs, and 3. parameter is dropped. If sting thid is the name of the js var
	 * @param array|string|null $attribsOrValue If first parameter is an array this is the attribs of the script tag. If first parameter is a string this is the value of the js var
	 * @param array|string|null $attribs If first parameter is an array this is dropped. If first parameter is a string this is the attribs of the script tag.
	 * @return void
	 */
	public function addScriptVars($dataOrKey, $attribsOrValue = null, $attribs = null) {
		if (is_array($dataOrKey)) {
			$data = $dataOrKey;
			$attribs = $attribsOrValue;
		} else {
			$data = array($dataOrKey => (string) $attribsOrValue);
		}

		if (isset($this->_scriptVarsHelpers[$attribs])) {
			$this->_scriptVarsHelpers[$attribs]->addVars($data);
		} else {
			$this->_scriptVarsHelpers[$attribs] = new Helper\ScriptVars($data, $attribs);
		}
	}

	/**
	 * Add meta tag to head addons.
	 * @access public
	 * @param array $attribs
	 * @return void
	 */
	public function addMeta($attribs) {
		$this->_addons["meta"][] = new Helper\Meta($attribs);
	}

	/**
	 * Add link tag to head addons.
	 * @access public
	 * @param array $attribs
	 * @return void
	 */
	public function addLink($attribs) {
		$this->_addons["link"][] = new Helper\Link($attribs);
	}

	/**
	 * Add script tag to head addons.
	 * @access public
	 * @param string $src
	 * @param array $attribs
	 * @return void
	 */
	public function addScript($src, $attribs = null) {
		if (!isset($this->_addons["script"][$src])) {
			$this->_addons["script"][$src] = array("position" => "head", "addon" => new Helper\Script((string) $src, $attribs));
		}
	}

	/**
	 * Add script before </body> tag.
	 * @access public
	 * @param string $src
	 * @param array $attribs
	 * @return void
	 */
	public function addBottomScript($src, $attribs = null) {
		if (!isset($this->_addons["script"][$src])) {
			$this->_addons["script"][$src] = array("position" => "bottom", "addon" => new Helper\Script((string) $src, $attribs));
		}
	}

	/**
	 * Add style tag to head addons.
	 * @access public
	 * @param string $href
	 * @param array $attribs
	 * @return void
	 */
	public function addStyle($href, $attribs = null) {
		$this->_addons["style"][$href] = new Helper\Style((string) $href, $attribs);
	}

	/**
	 * Add rss feed tag to head addons.
	 * @access public
	 * @param string $href
	 * @param string $title
	 * @return void
	 */
	public function addRssLink($href, $title = null) {
		$this->_addons["rssLink"] = new Helper\RssFeed((string) $href, (string) $title);
	}

	/**
	 * Returns output.
	 * @access public
	 * @return string
	 */
	public function getOutput() {
		$this->_checkView();

		$this->_createOutput();

		return $this->_output;
	}

	/**
	 * Return head addons
	 * @access protected
	 * @return string
	 */
	protected function _getHeadAddons() {
		$configReader = \Util\Config\Cache::getConfig(DIR_ROOT . "/config/Config.ini");

		$addons = "";
		if (!empty($this->_scriptVarsHelpers)) {
			foreach ($this->_scriptVarsHelpers as $addon) {
				$addons .= chr(9) . $addon->getHelper() . "\r\n";
			}
		}
		if (isset($this->_addons["meta"])) {
			foreach ($this->_addons["meta"] as $addon) {
				$addons .= chr(9) . $addon->getHelper() . "\r\n";
			}
		}
		if (isset($this->_addons["link"])) {
			foreach ($this->_addons["link"] as $addon) {
				$addons .= chr(9) . $addon->getHelper() . "\r\n";
			}
		}
		if (isset($this->_addons["style"])) {
			if (!is_null($configReader->MINIFY) && $configReader->MINIFY->css == true) {
				$helpers = array();
				foreach ($this->_addons["style"] as $src => $addon) {
					$helpers[$addon->getAttributes(true)][] = $addon->getHref();
				}
				if (!empty($helpers)) {
					foreach ($helpers as $attributes => $hrefs) {
						$addons .= chr(9) . '<link href="/minify/f=' . join(",", $hrefs) . '"' . $attributes . '/>' . "\r\n";
					}
				}
			} else {
				foreach ($this->_addons["style"] as $addon) {
					$addons .= chr(9) . $addon->getHelper() . "\r\n";
				}
			}
		}
		if (isset($this->_addons["rssLinks"])) {
			foreach ($this->_addons["rssLinks"] as $addon) {
				$addons .= chr(9) . $addon->getHelper() . "\r\n";
			}
		}
		if (isset($this->_addons["script"])) {
			if (!is_null($configReader->MINIFY) && $configReader->MINIFY->js == true) {
				$helpers = array();
				foreach ($this->_addons["script"] as $src => $addon) {
					if ($addon["position"] == "head") {
						$helpers[$addon["addon"]->getAttributes(true)][] = $addon["addon"]->getSrc();
					}
				}
				if (!empty($helpers)) {
					foreach ($helpers as $attributes => $hrefs) {
						$addons .= chr(9) . '<script src="/minify/f=' . join(",", $hrefs) . '"' . $attributes . '></script>' . "\r\n";
					}
				}
			} else {
				foreach ($this->_addons["script"] as $src => $addon) {
					if ($addon["position"] == "head") {
						$addons .= chr(9) . $addon["addon"]->getHelper() . "\r\n";
					}
				}
			}
		}
		if (isset($this->_addons["googleAnalytics"])) {
			$addons .= chr(9) . $this->_addons["googleAnalytics"]->getHelper() . "\r\n";
		}
		return $addons;
	}

	/**
	 * Modifies layout, setting virtual subdomains in inner resources.
	 * @access protected
	 * @param string
	 * @return string
	 */
	protected function _paralellizeResources($layout) {
		$configReader = \Util\Config\Cache::getConfig(DIR_ROOT . "/config/Config.ini");
		if ($configReader->PARALLELSUBDOMAINS->enabled == true) {
			$innerLinkMatches = array();
			$types = array();
			if ($configReader->PARALLELSUBDOMAINS->img != "") {
				$types[] = "img";
			}
			if ($configReader->PARALLELSUBDOMAINS->script != "") {
				$types[] = "script";
			}
			if ($configReader->PARALLELSUBDOMAINS->link != "") {
				$types[] = "link";
			}
			preg_match_all("/<(" . join("|", $types) . ").*?(href|src)[\s]*=[\s]*(\"(\/\S*)\"|'(\/\S*)').*?>/i", $layout, $innerLinkMatches);
			foreach ($innerLinkMatches[0] as $key => $htmlElement) {
				$link = ( $innerLinkMatches[4][$key] == "" ) ? $innerLinkMatches[5][$key] : $innerLinkMatches[4][$key];
				$hosts = explode(",", $configReader->PARALLELSUBDOMAINS->$innerLinkMatches[1][$key]);
				$host = $hosts[md5($link) % count($hosts)];
				$htmlElementNew = str_replace($link, "http://" . $host . "." . $_SERVER["HTTP_HOST"] . $link, $htmlElement);
				$layout = str_replace($htmlElement, $htmlElementNew, $layout);
			}
		}
		return $layout;
	}

	/**
	 * Return addons before </BODY> (bottomScripts)
	 * @access protected
	 * @return string
	 */
	protected function _getBottomAddons() {

		$addons = "";
		if (isset($this->_addons["script"])) {
			$configReader = \Util\Config\Cache::getConfig(DIR_ROOT . "/config/Config.ini");
			if (!is_null($configReader->MINIFY) && $configReader->MINIFY->js == true) {
			
				$helpers = array();
				foreach ($this->_addons["script"] as $src => $addon) {
					if ($addon["position"] == "bottom") {
						$helpers[$addon["addon"]->getAttributes(true)][] = $addon["addon"]->getSrc();
					}
				}
				if (!empty($helpers)) {
					foreach ($helpers as $attributes => $hrefs) {
						$addons .= chr(9) . '<script src="/minify/f=' . join(",", $hrefs) . '"' . $attributes . '></script>' . "\r\n";
					}
				}
			} else {
				foreach ($this->_addons["script"] as $src => $addon) {
					if ($addon["position"] == "bottom") {
						$addons .= chr(9) . $addon["addon"]->getHelper() . "\r\n";
					}
				}				
			}
		}
		return $addons;
	}

	/**
	 * Check view for create output.
	 * @access protected
	 * @return void
	 */
	protected function _checkView() {
		if (!file_exists($this->_templateDirectory . "/" . $this->_template)) {
			throw new TemplateNotFoundException("Template ($this->_template) not found");
		}

		if (($this->_layout !== "") && (!file_exists($this->_templateDirectory . "/layouts/" . $this->_layout))) {
			throw new TemplateNotFoundException("Layout (/layouts/$this->_layout) not found");
		}
	}

	/**
	 * Create output.
	 * @access protected
	 * @return void
	 */
	abstract protected function _createOutput();
}

?>